// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraHealthSet.h"
#include "AbilitySystem/Attributes/LyraAttributeSet.h"
#include "LyraGameplayTags.h"
#include "Net/UnrealNetwork.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Engine/World.h"
#include "GameplayEffectExtension.h"
#include "Messages/LyraVerbMessage.h"
#include "GameFramework/GameplayMessageSubsystem.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraHealthSet) // 包含由UE生成的内联代码

// 定义游戏玩法标签
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_Damage, "Gameplay.Damage"); // 定义标签：Gameplay.Damage
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_DamageImmunity, "Gameplay.DamageImmunity"); // 定义标签：Gameplay.DamageImmunity
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_DamageSelfDestruct, "Gameplay.Damage.SelfDestruct"); // 定义标签：Gameplay.Damage.SelfDestruct
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_FellOutOfWorld, "Gameplay.Damage.FellOutOfWorld"); // 定义标签：Gameplay.Damage.FellOutOfWorld
UE_DEFINE_GAMEPLAY_TAG(TAG_Lyra_Damage_Message, "Lyra.Damage.Message"); // 定义标签：Lyra.Damage.Message

/**
 * ULyraHealthSet的构造函数
 * 初始化健康属性相关变量
 */
ULyraHealthSet::ULyraHealthSet()
	: Health(100.0f) // 初始化生命值为100.0
	, MaxHealth(100.0f) // 初始化最大生命值为100.0
{
	bOutOfHealth = false; // 初始化为未耗尽生命值
	MaxHealthBeforeAttributeChange = 0.0f; // 初始化属性改变前的最大生命值为0.0
	HealthBeforeAttributeChange = 0.0f; // 初始化属性改变前的生命值为0.0
}

/**
 * 设置属性的网络复制生命周期
 * @param OutLifetimeProps 生命周期属性数组
 */
void ULyraHealthSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 调用父类方法

	// 设置Health属性的复制条件：无条件，总是通知
	DOREPLIFETIME_CONDITION_NOTIFY(ULyraHealthSet, Health, COND_None, REPNOTIFY_Always);
	// 设置MaxHealth属性的复制条件：无条件，总是通知
	DOREPLIFETIME_CONDITION_NOTIFY(ULyraHealthSet, MaxHealth, COND_None, REPNOTIFY_Always);
}

/**
 * Health属性网络复制回调函数
 * @param OldValue 旧的属性值
 */
void ULyraHealthSet::OnRep_Health(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(ULyraHealthSet, Health, OldValue); // 通知属性复制

	// 调用变更回调，但没有发起者
	// 未来可以改为显式RPC
	// 客户端上的这些事件不应更改属性

	const float CurrentHealth = GetHealth(); // 获取当前生命值
	const float EstimatedMagnitude = CurrentHealth - OldValue.GetCurrentValue(); // 计算估计的变化量
	
	// 广播生命值改变事件
	OnHealthChanged.Broadcast(nullptr, nullptr, nullptr, EstimatedMagnitude, OldValue.GetCurrentValue(), CurrentHealth);

	// 如果之前未耗尽生命值且当前生命值<=0，则广播耗尽生命值事件
	if (!bOutOfHealth && CurrentHealth <= 0.0f)
	{
		OnOutOfHealth.Broadcast(nullptr, nullptr, nullptr, EstimatedMagnitude, OldValue.GetCurrentValue(), CurrentHealth);
	}

	bOutOfHealth = (CurrentHealth <= 0.0f); // 更新耗尽生命值状态
}

/**
 * MaxHealth属性网络复制回调函数
 * @param OldValue 旧的属性值
 */
void ULyraHealthSet::OnRep_MaxHealth(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(ULyraHealthSet, MaxHealth, OldValue); // 通知属性复制

	// 调用变更回调，但没有发起者
	// 未来可以改为显式RPC
	// 广播最大生命值改变事件
	OnMaxHealthChanged.Broadcast(nullptr, nullptr, nullptr, GetMaxHealth() - OldValue.GetCurrentValue(), OldValue.GetCurrentValue(), GetMaxHealth());
}

/**
 * 游戏效果执行前的预处理
 * @param Data 游戏效果修改回调数据
 * @return 是否继续执行游戏效果
 */
bool ULyraHealthSet::PreGameplayEffectExecute(FGameplayEffectModCallbackData &Data)
{
	if (!Super::PreGameplayEffectExecute(Data)) // 调用父类方法
	{
		return false; // 父类处理失败，返回false
	}

	// 处理修改传入的普通伤害
	if (Data.EvaluatedData.Attribute == GetDamageAttribute()) // 如果评估的属性是伤害属性
	{
		if (Data.EvaluatedData.Magnitude > 0.0f) // 如果伤害量大于0
		{
			const bool bIsDamageFromSelfDestruct = Data.EffectSpec.GetDynamicAssetTags().HasTagExact(TAG_Gameplay_DamageSelfDestruct); // 检查是否是自毁伤害

			// 如果目标有伤害免疫标签且不是自毁伤害，则免疫伤害
			if (Data.Target.HasMatchingGameplayTag(TAG_Gameplay_DamageImmunity) && !bIsDamageFromSelfDestruct)
			{
				// 不减少任何生命值。
				Data.EvaluatedData.Magnitude = 0.0f; // 将伤害量设置为0
				return false; // 返回false，不执行伤害效果
			}

// 在非发布版本中检查上帝模式作弊
#if !UE_BUILD_SHIPPING
			// 检查上帝模式作弊，无限生命值在下面检查
			if (Data.Target.HasMatchingGameplayTag(LyraGameplayTags::Cheat_GodMode) && !bIsDamageFromSelfDestruct)
			{
				// 不减少任何生命值。
				Data.EvaluatedData.Magnitude = 0.0f; // 将伤害量设置为0
				return false; // 返回false，不执行伤害效果
			}
#endif // #if !UE_BUILD_SHIPPING
		}
	}

	// 保存当前的生命值
	HealthBeforeAttributeChange = GetHealth(); // 保存属性改变前的生命值
	MaxHealthBeforeAttributeChange = GetMaxHealth(); // 保存属性改变前的最大生命值

	return true; // 返回true，继续执行游戏效果
}

/**
 * 游戏效果执行后的后处理
 * @param Data 游戏效果修改回调数据
 */
void ULyraHealthSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data); // 调用父类方法

	const bool bIsDamageFromSelfDestruct = Data.EffectSpec.GetDynamicAssetTags().HasTagExact(TAG_Gameplay_DamageSelfDestruct); // 检查是否是自毁伤害
	float MinimumHealth = 0.0f; // 最小生命值，默认为0

// 在非发布版本中处理上帝模式和无限生命值
#if !UE_BUILD_SHIPPING
	// 上帝模式和无限生命值阻止死亡，除非是自毁
	if (!bIsDamageFromSelfDestruct &&
		(Data.Target.HasMatchingGameplayTag(LyraGameplayTags::Cheat_GodMode) || Data.Target.HasMatchingGameplayTag(LyraGameplayTags::Cheat_UnlimitedHealth) ))
	{
		MinimumHealth = 1.0f; // 设置最小生命值为1，防止死亡
	}
#endif // #if !UE_BUILD_SHIPPING

	const FGameplayEffectContextHandle& EffectContext = Data.EffectSpec.GetEffectContext(); // 获取游戏效果上下文
	AActor* Instigator = EffectContext.GetOriginalInstigator(); // 获取效果发起者
	AActor* Causer = EffectContext.GetEffectCauser(); // 获取效果引起者

	if (Data.EvaluatedData.Attribute == GetDamageAttribute()) // 如果评估的属性是伤害属性
	{
		// 发送一个标准化的动词消息，其他系统可以观察
		if (Data.EvaluatedData.Magnitude > 0.0f) // 如果伤害量大于0
		{
			FLyraVerbMessage Message; // 创建Lyra动词消息
			Message.Verb = TAG_Lyra_Damage_Message; // 设置动词标签
			Message.Instigator = Data.EffectSpec.GetEffectContext().GetEffectCauser(); // 设置发起者
			Message.InstigatorTags = *Data.EffectSpec.CapturedSourceTags.GetAggregatedTags(); // 设置发起者标签
			Message.Target = GetOwningActor(); // 设置目标
			Message.TargetTags = *Data.EffectSpec.CapturedTargetTags.GetAggregatedTags(); // 设置目标标签
			//@TODO: 填充上下文标签，以及任何非能力系统的源/发起者标签
			//@TODO: 确定是否是对立团队击杀、自我击杀、团队击杀等...
			Message.Magnitude = Data.EvaluatedData.Magnitude; // 设置伤害量

			UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld()); // 获取游戏消息子系统
			MessageSystem.BroadcastMessage(Message.Verb, Message); // 广播消息
		}

		// 转换为-生命值然后钳制
		SetHealth(FMath::Clamp(GetHealth() - GetDamage(), MinimumHealth, GetMaxHealth())); // 计算新生命值并钳制
		SetDamage(0.0f); // 重置伤害属性为0
	}
	else if (Data.EvaluatedData.Attribute == GetHealingAttribute()) // 如果评估的属性是治疗属性
	{
		// 转换为+生命值然后钳制
		SetHealth(FMath::Clamp(GetHealth() + GetHealing(), MinimumHealth, GetMaxHealth())); // 计算新生命值并钳制
		SetHealing(0.0f); // 重置治疗属性为0
	}
	else if (Data.EvaluatedData.Attribute == GetHealthAttribute()) // 如果评估的属性是生命值属性
	{
		// 钳制并进入下面的生命值耗尽处理
		SetHealth(FMath::Clamp(GetHealth(), MinimumHealth, GetMaxHealth())); // 钳制生命值
	}
	else if (Data.EvaluatedData.Attribute == GetMaxHealthAttribute()) // 如果评估的属性是最大生命值属性
	{
		// TODO 钳制当前生命值？

		// 在任何请求的最大生命值更改时通知
		OnMaxHealthChanged.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, MaxHealthBeforeAttributeChange, GetMaxHealth()); // 广播最大生命值改变事件
	}

	// 如果生命值实际发生变化，激活回调
	if (GetHealth() != HealthBeforeAttributeChange) // 如果生命值发生了改变
	{
		OnHealthChanged.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, HealthBeforeAttributeChange, GetHealth()); // 广播生命值改变事件
	}

	// 如果生命值<=0且之前未耗尽生命值，则广播耗尽生命值事件
	if ((GetHealth() <= 0.0f) && !bOutOfHealth)
	{
		OnOutOfHealth.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, HealthBeforeAttributeChange, GetHealth()); // 广播耗尽生命值事件
	}

	// 再次检查生命值，以防上面的事件改变了它。
	bOutOfHealth = (GetHealth() <= 0.0f); // 更新耗尽生命值状态
}

/**
 * 属性基础值改变前的预处理
 * @param Attribute 要改变的属性
 * @param NewValue 新的属性值
 */
void ULyraHealthSet::PreAttributeBaseChange(const FGameplayAttribute& Attribute, float& NewValue) const
{
	Super::PreAttributeBaseChange(Attribute, NewValue); // 调用父类方法

	ClampAttribute(Attribute, NewValue); // 钳制属性值
}

/**
 * 属性当前值改变前的预处理
 * @param Attribute 要改变的属性
 * @param NewValue 新的属性值
 */
void ULyraHealthSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	Super::PreAttributeChange(Attribute, NewValue); // 调用父类方法

	ClampAttribute(Attribute, NewValue); // 钳制属性值
}

/**
 * 属性改变后的后处理
 * @param Attribute 改变的属性
 * @param OldValue 旧的属性值
 * @param NewValue 新的属性值
 */
void ULyraHealthSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	Super::PostAttributeChange(Attribute, OldValue, NewValue); // 调用父类方法

	// 如果改变的是最大生命值属性
	if (Attribute == GetMaxHealthAttribute())
	{
		// 确保当前生命值不大于新的最大生命值。
		if (GetHealth() > NewValue) // 如果当前生命值大于新的最大生命值
		{
			ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent(); // 获取Lyra能力系统组件
			check(LyraASC); // 检查指针有效性

			// 应用覆盖修改到生命值属性，将其设置为新的最大生命值
			LyraASC->ApplyModToAttribute(GetHealthAttribute(), EGameplayModOp::Override, NewValue);
		}
	}

	// 如果之前生命值耗尽且当前生命值>0，则重置耗尽状态
	if (bOutOfHealth && (GetHealth() > 0.0f))
	{
		bOutOfHealth = false; // 重置为未耗尽生命值
	}
}

/**
 * 限制属性值的辅助函数
 * @param Attribute 要限制的属性
 * @param NewValue 新的属性值
 */
void ULyraHealthSet::ClampAttribute(const FGameplayAttribute& Attribute, float& NewValue) const
{
	if (Attribute == GetHealthAttribute()) // 如果是生命值属性
	{
		// 不允许生命值变为负数或超过最大生命值。
		NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth()); // 钳制到[0, MaxHealth]范围
	}
	else if (Attribute == GetMaxHealthAttribute()) // 如果是最大生命值属性
	{
		// 不允许最大生命值低于1。
		NewValue = FMath::Max(NewValue, 1.0f); // 确保至少为1
	}
}